package com.panda.utils;

import com.google.common.base.Joiner;
import com.panda.expand.Extend;
import com.panda.pojo.*;

import java.util.*;

class ProcessUtils {


    static void process(Context context) {

        Element rootElement = context.getRootElement();

        List<Block> subElement = rootElement.getSubBlocks();

        StringBuilder builder = new StringBuilder();

        subElement.forEach(builder::append);
        context.setText(builder.toString());
    }


    //深度加工
    static void deepProcess(Context context, Map config) {

        Element html = new Html(context, config);

        //创建新的根元素,防止无限递归
        Element element = new Element(context, "__root__");

        element.setSubBlocks(new ArrayList<>());

        element.addSubBlock(html);

        context.setRootElement(element);

        //加工
        process(context);

        //字符串回填
        context.setText(StringUtils.fillBack(context.getStringMap(), context.getText()));

    }

    private static class Html extends Element {


        private Map config;

        private Element source;

        Html(Context context, Map config) {

            super(context, "html");

            this.config = config;

            source = context.take("Html");

            //如果没有html标签,就将root标签视为根标签
            if (source == null) {
                source = context.getRootElement();
            }


        }


        private String root() {

            subBlocks = new LinkedList<>();


            //找到head标签,把依赖的css文件添加进head文件中
            Element head = source.take("Head");

            //如果没有就创建
            if (head == null) {
                head = new Element(context, "head");
                head.setSubBlocks(new ArrayList<>());
            }


            Element body = source.take("Body");

            if (body == null) {
                body = new Element(context, "body");
                body.setSubBlocks(new ArrayList<>());
            }


            //将source中其他的内容添加进body中
            List<Block> sourceSubBlocks = source.getSubBlocks();

            for (Block block : sourceSubBlocks) {

                body.addSubBlock(block);
            }

            //将head和body添加给该html
            addSubBlock(head);

            addSubBlock(body);


            //矫正context
            setContext(context);

            //先将script取出,再分析依赖
            List<Element> scripts = new LinkedList<>();

            while (true) {

                Element script = take("Script");

                if (script == null) {
                    break;
                } else {

                    if (script.getScope().getScript() != null && !script.getScope().getScript().equals("")) {
                        List<Block> subBlocks = script.getSubBlocks();
                        subBlocks.clear();

                        StringBlock stringBlock = new StringBlock();
                        stringBlock.setText(script.getScope().getScript());
                        subBlocks.add(stringBlock);

                        script.getAttributes().remove("ng-controller");

                        scripts.add(script);
                    }


                }


            }


            //分析服务或文件的依赖
            handleDep();


            //jQuery和angularJs两个必要的库

            Element jQuery = new Element(context, "script");

            jQuery.setSubBlocks(Collections.emptyList());

            jQuery.getAttributes().put("src", config.get("jQuery") + "");

            addSubBlock(jQuery);

            Element angularJs = new Element(context, "script");

            angularJs.setSubBlocks(Collections.emptyList());

            angularJs.getAttributes().put("src", config.get("angularJs") + "");

            addSubBlock(angularJs);

            //将脚本写在两个必要的库依赖之前
            for (Element script : scripts) {
                addSubBlock(script);
            }

            //添加css文件
            Set<String> css = context.getCss();
            Set<String> realCss = new HashSet<>();

            for (String s : css) {

                s = s.trim();

                if (s.startsWith("'") || s.startsWith("\"")) {
                    realCss.add(s.substring(1, s.length() - 1));
                } else {

                    Map map = (Map) context.getCssFiles().get(s);

                    if (map != null) {

                        String cssPath = (String) (map.get("css"));
                        if (cssPath == null || cssPath.trim().equals("")) {
                            throw new RuntimeException("需要依赖css文件,但是没有相应的配置: " + s);
                        } else {
                            realCss.add(cssPath);
                        }
                    } else {
                        throw new RuntimeException("需要依赖css文件,但是没有相应的配置: " + s);
                    }

                }

            }

            for (String s : realCss) {
                Element style = new Element(context, "link");
                style.getAttributes().put("href", s);
                style.getAttributes().put("rel", "stylesheet");
                style.getAttributes().put("type", "text/css");
                head.addSubBlock(style);

            }


            //获取Js树
            Set<String> js = context.getJs();
            Set<String> realJs = new HashSet<>();
            for (String j : js) {

                j = j.trim();


                if (j.startsWith("'") || j.startsWith("\"")) {
                    realJs.add(j.substring(1, j.length() - 1));
                } else {

                    Map map = (Map) context.getJsFiles().get(j);
                    if (map != null) {
                        String jsPath = (String) (map.get("js"));
                        if (jsPath == null || jsPath.trim().equals("")) {
                            throw new RuntimeException("需要依赖js文件,但是没有相应的配置: " + j);
                        }

                        realJs.add(jsPath);

                    } else {
                        throw new RuntimeException("需要依赖js文件,但是没有相应的配置: " + j);
                    }

                }

            }


            for (String j : realJs) {

                Element script = new Element(context, "script");

                script.setSubBlocks(Collections.emptyList());
                script.getAttributes().put("src", j);

                addSubBlock(script);

            }

            //添加ng-app等
            body.getAttributes().put("ng-app", context.getAppName());

            StringBuilder builder = new StringBuilder("<" + name);

            Map<String, String> stringMap = context.getStringMap();

            //解析自己的属性
            attributes.forEach((k, v) -> {

                if (v == null) {
                    builder.append(" ").append(k);
                } else {

                    if (stringMap.containsKey(v)) {
                        builder.append(" ").append(k).append("=").append(v);
                    } else {
                        builder.append(" ").append(k).append("=").append("\"").append(v).append("\"");
                    }

                }


            });


            builder.append(">").append("\n");

            //遍历自己的子元素
            for (Block aSubElement : subBlocks) {
                builder.append(aSubElement.toString());
            }

            //添加控制器
            Set<Scope> controllerScopes = context.getControllerScopes();

            //创建模块
            StringBuilder cs = new StringBuilder("var module = angular.module('" + context.getAppName() + "', []);\n");

            Set<Extend> extendSet = ExpandInit.getExtendSet();

            for (Extend extend : extendSet) {
                cs.append(extend.toString());
            }

            //控制器登记器
            cs.append("module.config(['$controllerProvider',function($controllerProvider){module.register={controller:$controllerProvider.register};}]);module.config(['$provide',function($provide){module.provide=$provide;}]);module.loadedView=[];module.directive('subView',function(){return{restrict:'A',templateUrl:function(element,attre){return attre.subView;},compile:function(element,attre){var vw=$(element);if(module.loadedView.indexOf(attre.subView)===-1){module.loadedView.push(attre.subView);var ss=vw.children('service');for(i=0;i<ss.length;i++){eval(\"module.provide.\"+ss[i].textContent)}var cs=vw.children('controller');for(i=0;i<cs.length;i++){eval(\"module.register.\"+cs[i].textContent);}var js=vw.children('js');for(i=0;i<js.length;i++){if(js[i].attributes.src){$.getScript(js[i].attributes.src.value);}else{$.globalEval(js[i].textContent);}}}vw.children('js').remove();vw.children('service').remove();vw.children('controller').remove();}}});");

            //添加服务声明
            Set<Service> serviceSet = context.getServices();

            serviceSet.forEach(service -> cs.append("\nmodule.service('").append(service.getName()).append("', ").append(service.toString()).append(");\n"));


            //需要创建Controller的作用域
            controllerScopes.forEach(scope -> {

                String scopeScript = scope.getScript();

                cs.append("\n");

                Set<String> services = scope.getDependencies();

                cs.append("\nmodule.controller('").append(scope.getId()).append("', function (").append(Joiner.on(",").join(services)).append(") {\n\n").append(scopeScript).append("\n\n});");


            });


            StringBlock stringBlock = new StringBlock();

            stringBlock.setText(cs.toString());

            Element script = new Element(context, "script");

            script.setSubBlocks(new ArrayList<>());

            script.addSubBlock(stringBlock);

            //追加为脚本
            builder.append(script.toString());

            //封口
            builder.append("</").append(name).append(">").append("\n");

            return builder.toString().replaceAll("(?<=\\{)[ \n]+(?=\\{)", "").replaceAll("(?<=})[ \n]+(?=})", "");

        }


        private String view() {

            subBlocks = new LinkedList<>();

            //找到head标签,把依赖的css文件添加进head文件中
            Element head = source.take("Head");

            //如果没有就创建
            if (head == null) {
                head = new Element(context, "head");
                head.setSubBlocks(new ArrayList<>());
            }


            Element body = source.take("Body");

            if (body == null) {
                body = new Element(context, "body");
                body.setSubBlocks(new ArrayList<>());
            }


            //将source中其他的内容添加进body中
            List<Block> sourceSubBlocks = source.getSubBlocks();

            for (Block block : sourceSubBlocks) {

                body.addSubBlock(block);
            }

            //将head和body添加给该html
            addSubBlock(head);

            addSubBlock(body);


            //矫正context
            setContext(context);

            //先将script取出,再分析依赖
            List<Element> scripts = new LinkedList<>();

            while (true) {

                Element script = take("Script");

                if (script == null) {
                    break;
                } else {

                    script.setName("js");

                    if (script.getScope().getScript() != null && !script.getScope().getScript().equals("")) {
                        List<Block> subBlocks = script.getSubBlocks();
                        subBlocks.clear();

                        StringBlock stringBlock = new StringBlock();
                        stringBlock.setText(script.getScope().getScript());
                        subBlocks.add(stringBlock);

                        script.getAttributes().remove("ng-controller");

                        script.getAttributes().put("style","display:none");

                        scripts.add(script);
                    }


                }


            }


            //分析服务或文件的依赖
            handleDep();

            for (Element script : scripts) {
                addSubBlock(script);
            }

            //添加css文件
            Set<String> css = context.getCss();
            Set<String> realCss = new HashSet<>();

            for (String s : css) {

                s = s.trim();

                if (s.startsWith("'") || s.startsWith("\"")) {
                    realCss.add(s.substring(1, s.length() - 1));
                } else {

                    Map map = (Map) context.getCssFiles().get(s);

                    if (map != null) {

                        String cssPath = (String) (map.get("css"));
                        if (cssPath == null || cssPath.trim().equals("")) {
                            throw new RuntimeException("需要依赖css文件,但是没有相应的配置: " + s);
                        } else {
                            realCss.add(cssPath);
                        }
                    } else {
                        throw new RuntimeException("需要依赖css文件,但是没有相应的配置: " + s);
                    }

                }

            }

            for (String s : realCss) {
                Element style = new Element(context, "link");
                style.getAttributes().put("href", s);
                style.getAttributes().put("rel", "stylesheet");
                style.getAttributes().put("type", "text/css");
                head.addSubBlock(style);

            }


            //获取Js树
            Set<String> js = context.getJs();
            Set<String> realJs = new HashSet<>();
            for (String j : js) {

                j = j.trim();


                if (j.startsWith("'") || j.startsWith("\"")) {
                    realJs.add(j.substring(1, j.length() - 1));
                } else {

                    Map map = (Map) context.getJsFiles().get(j);
                    if (map != null) {
                        String jsPath = (String) (map.get("js"));
                        if (jsPath == null || jsPath.trim().equals("")) {
                            throw new RuntimeException("需要依赖js文件,但是没有相应的配置: " + j);
                        }

                        realJs.add(jsPath);

                    } else {
                        throw new RuntimeException("需要依赖js文件,但是没有相应的配置: " + j);
                    }

                }

            }


            for (String j : realJs) {

                Element script = new Element(context, "js");

                script.setSubBlocks(Collections.emptyList());
                script.getAttributes().put("src", j);

                addSubBlock(script);

            }

            //添加ng-app等
            body.getAttributes().put("ng-app", context.getAppName());

            StringBuilder builder = new StringBuilder("<" + name);

            builder.append(">").append("\n");

            //遍历自己的子元素
            for (Block aSubElement : subBlocks) {
                builder.append(aSubElement.toString());
            }

            StringBuilder stringBuilder = new StringBuilder();


            Set<Service> serviceSet = context.getServices();

            serviceSet.forEach(service -> {
                Element s = new Element(context, "service");

                stringBuilder.append("\nservice('").append(service.getName()).append("', ").append(service.toString()).append(");\n");

                s.setSubBlocks(new ArrayList<>());

                s.addSubBlock(new StringBlock(stringBuilder.toString()));

                s.getAttributes().put("style","display:none");

                builder.append(s.toString());

                stringBuilder.delete(0, stringBuilder.length());

            });


            //添加控制器
            Set<Scope> controllerScopes = context.getControllerScopes();


            controllerScopes.forEach(scope -> {

                Element controller = new Element(context, "controller");

                String scopeScript = scope.getScript();

                Set<String> services = scope.getDependencies();

                stringBuilder.append("\ncontroller('").append(scope.getId()).append("', function (").append(Joiner.on(",").join(services)).append(") {\n\n").append(scopeScript).append("\n\n});");

                controller.setSubBlocks(new ArrayList<>());

                controller.addSubBlock(new StringBlock(stringBuilder.toString()));

                controller.getAttributes().put("style","display:none");

                builder.append(controller.toString());

                stringBuilder.delete(0, stringBuilder.length());

            });


            //封口
            builder.append("</").append(name).append(">").append("\n");

            return builder.toString().replaceAll("(?<=\\{)[ \n]+(?=\\{)", "").replaceAll("(?<=})[ \n]+(?=})", "");


        }


        @Override
        public String toString() {

            if (context.isView()) {
                return view();
            } else {
                return root();
            }

        }


    }

}
